package wwiii;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class MapGenerator {
	protected final int areas;
	protected final int sizex;
	protected final int sizey;
	protected final int areaSize;
	protected final int areaAreaDistance;
	protected final int areaLinkDistance;

	public MapGenerator(int areas, int sizex, int sizey, int areaSize) {
		this(areas, sizex, sizey, areaSize, areaSize+areaSize, areaSize);
	}
	public MapGenerator(int areas, int sizex, int sizey, int areaSize, int areaAreaDistance, int areaLinkDistance) {
		this.areas = areas;
		this.sizex = sizex;
		this.sizey = sizey;
		this.areaSize = areaSize;
		this.areaAreaDistance = areaAreaDistance;
		this.areaLinkDistance = areaLinkDistance;
	}

	public AreaMap generate() {
		final AreaMap map = new AreaMap(sizex, sizey, areaSize);

		createAreas(map);
		final Collection<Link> links = createLinks(map);

		filterCloseToAreas(map.getAreas(), links);
		filterCrossingPaths(links);

		for (Link link : links) {
			if (!link.isOK()) continue;

			link.a.addTarget(link.b);
			link.b.addTarget(link.a);
		}

		return map;
	}

	public void createAreas(AreaMap map) {
		int count = 0;

		while (map.getAreaCount() < areas) {
			if (count++ > 1000) break;

			String name = "area"+ map.getAreaCount();
			int x = (int) (Math.random()*(sizex - areaSize*2)+ areaSize);
			int y = (int) (Math.random()*(sizey - areaSize*2)+ areaSize);
			Player owner = new Player("id", "mail", "name", 0);
			int force = 0;

			if (testArea(map, x, y)) {
				map.addArea(new Area(name, x, y, owner, force));
			}
		}
	}

	public boolean testArea(AreaMap map, int x, int y) {
		int minL2 = Integer.MAX_VALUE;
		for (Area area : map.getAreas()) {
			int dx = x - area.getX();
			int dy = y - area.getY();
			int l2 = dx*dx + dy*dy;
			if (l2 < minL2) {
				minL2 = l2;
			}
		}

		return minL2 > areaAreaDistance*areaAreaDistance;
	}

	public Collection<Link> createLinks(AreaMap map) {
		final Collection<Link> links = new ArrayList<Link>();

		for (Area area1 : map.getAreas()) {
			for (Area area2 : map.getAreas()) {
				if (area1 == area2) break;

				final Link link = new Link(area1, area2);
				links.add(link);
			}
		}

		return links;
	}

	public void filterCrossingPaths(Collection<Link> links) {
		final List<Link> list = new ArrayList<Link>(links);
		// Sort by length
		Collections.sort(list, new Comparator<Link>() {
			@Override
			public int compare(Link link1, Link link2) {
				return link1.l2 - link2.l2;
			}
		});

		// Remove longest of crossing links
		for (Link link1 : list) {
			for (Link link2 : list) {
				if (link1 == link2) break;

				if (link1.a == link2.a) continue;
				if (link1.a == link2.b) continue;
				if (link1.b == link2.a) continue;
				if (link1.b == link2.b) continue;
				if (!link2.isOK()) continue;

				double x1 = link1.a.getX(), y1 = link1.a.getY();
				double x2 = link1.b.getX(), y2 = link1.b.getY();
				double x3 = link2.a.getX(), y3 = link2.a.getY();
				double x4 = link2.b.getX(), y4 = link2.b.getY();

				double ua = ((x4-x3)*(y1-y3)-(y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
				double ub = ((x2-x1)*(y1-y3)-(y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));

				if (ua < 0 || ua > 1) continue;
				if (ub < 0 || ub > 1) continue;

				link1.crossedBy = link2;
				break;
			}
		}
	}

	public void filterCloseToAreas(Collection<Area> areas, Collection<Link> links) {
		for (Link link : links) {
			for (Area area : areas) {
				if (area == link.a) continue;
				if (area == link.b) continue;

				double x1 = link.a.getX(), y1 = link.a.getY();
				double x2 = link.b.getX(), y2 = link.b.getY();
				double x3 = area.getX(), y3 = area.getY();
				double dx12 = x2 - x1, dy12 = y2 - y1;
				double u = ((x3-x1)*(x2-x1) + (y3-y1)*(y2-y1)) / (dx12*dx12 + dy12*dy12);

				if (u < 0 || u > 1) continue;

				double x = x1 + u*(x2 - x1);
				double y = y1 + u*(y2 - y1);
				double dx3x = x3 - x, dy3y = y3 - y;
				double d2 = dx3x*dx3x + dy3y*dy3y;

				if (d2 < areaLinkDistance * areaLinkDistance) {
					link.closeTo = area;
					break;
				}
			}
		}
	}
}
